﻿//Copyright 2011 - Jonathan Allen

using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Collections.ObjectModel;
using Granite.Eventing;
using System.Diagnostics.Contracts;
using System.Diagnostics.CodeAnalysis;
using Granite.Modeling.Internals;
using Granite.DataAnnotations;

namespace Granite.Modeling
{



	/// <summary>
	/// Abstract base class to deal with the limitations of generics
	/// </summary>
	public abstract partial class ModelBase : INotifyPropertyChanged, INotifyPropertyChangedWeak
	{
		readonly PropertyChangedEventManager m_PropertyChangedEventManager;
		readonly ErrorsDictionary m_Errors = new ErrorsDictionary();

		/// <summary>
		/// 
		/// </summary>
		[Pure]
		protected ModelBase()
		{
			m_PropertyChangedEventManager = new PropertyChangedEventManager(this);
		}

		/// <summary>
		/// Raised when the errors collection has changed. 
		/// </summary>
		/// <remarks>This may be fired even when no actual change has occurred.</remarks>
		public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

		/// <summary>
		/// Occurs when a property value changes.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Adds a weak event handler.
		/// </summary>
		/// <param name="eventHandler"></param>
		public void AddHandler(IListener<PropertyChangedEventArgs> eventHandler)
		{
			if (eventHandler == null)
				throw new ArgumentNullException("eventHandler", "eventHandler is null.");
			Contract.EndContractBlock();

			m_PropertyChangedEventManager.AddHandler(eventHandler);
		}

		/// <summary>
		/// Removes a weak event handler.
		/// </summary>
		/// <param name="eventHandler"></param>
		public void RemoveHandler(IListener<PropertyChangedEventArgs> eventHandler)
		{
			if (eventHandler == null)
				throw new ArgumentNullException("eventHandler", "eventHandler is null.");
			Contract.EndContractBlock();

			m_PropertyChangedEventManager.RemoveHandler(eventHandler);
		}

		/// <summary>
		/// Triggers the PropertyChanged event.
		/// </summary>
		internal void InvokePropertyChanged(PropertyChangedEventArgs e)
		{
			Contract.Requires(e != null);

			if (PropertyChanged != null)
				PropertyChanged(this, e);
		}

		///// <summary>
		///// Triggers the PropertyChanged event.
		///// </summary>
		//internal void InvokePropertyChanged(string propertyName, object oldValue, object newValue)
		//{
		//	if (PropertyChanged != null)
		//		PropertyChanged(this, new DetailedPropertyChangedEventArgs(propertyName, oldValue, newValue));
		//}

		/// <summary>
		/// Override this method to add imperative validation at the object level.
		/// </summary>
		/// <param name="results">A collection of the declarative validation errors. You may add and remove errors from this collection.</param>
		[Pure]
		protected virtual void OnValidateObject(ValidationResultCollection results) { }

		/// <summary>
		/// Override this method to add imperative validation at the property level.
		/// </summary>
		/// <param name="propertyName">The name of the property being validated.</param>
		/// <param name="results">A collection of the declarative validation errors. You may add and remove errors from this collection.</param>
		[Pure]
		protected virtual void OnValidateProperty(string propertyName, ValidationResultCollection results) { }


		[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode"), SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
		[ContractInvariantMethod]
		void ObjectInvariant()
		{
			Contract.Invariant(m_PropertyChangedEventManager != null);
			Contract.Invariant(m_Errors != null);
		}

		internal ErrorsDictionary ErrorsDictionary
		{
			get
			{
				Contract.Ensures(Contract.Result<ErrorsDictionary>() != null);

				return m_Errors;
			}
		}

		/// <summary>
		/// Invoke this method to signal the events associated with changing the errors dictionary. The parameter updateType is returned by the methods on ErrorsDictionary.
		/// </summary>
		/// <param name="propertyName"></param>
		/// <param name="updateType"></param>
		internal void OnErrorsChanged(string propertyName, ErrorsDictionaryUpdateType updateType)
		{
			if (updateType == ErrorsDictionaryUpdateType.NoChange)
				return;

			OnErrorsChanged(propertyName);

			if (updateType == ErrorsDictionaryUpdateType.HasErrorsIsFalse || updateType == ErrorsDictionaryUpdateType.HasErrorsIsTrue)
				InvokePropertyChanged(CommonProperties.HasErrorsProperty);
		}

		/// <summary>
		/// Used to manually invoke the ErrorsChanged event.
		/// </summary>
		/// <param name="propertyName"></param>
		protected void OnErrorsChanged(string propertyName)
		{
			if (ErrorsChanged != null)
				ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
		}


	}




}
